#pragma once

#include <iostream>
#include <vector>
#include <string>
#include <semaphore.h>

using namespace std;

const int gCap = 10;

template <class T>
class RingQueue
{
public:
    RingQueue(int cap = gCap) : ringqueue_(cap), pIndex_(0), cIndex_(0)
    {
        sem_init(&roomSem_, 0, ringqueue_.size());
        sem_init(&dataSem_, 0, 0);

        pthread_mutex_init(&pmutex_, nullptr);
        pthread_mutex_init(&cmutex_, nullptr);
    }

    void push(const T &in)
    {
        sem_wait(&roomSem_);
        pthread_mutex_lock(&pmutex_);

        ringqueue_[pIndex_] = in;
        pIndex_++;
        pIndex_ %= ringqueue_.size();

        pthread_mutex_unlock(&pmutex_);
        sem_post(&dataSem_);
    }

    T pop()
    {
        sem_wait(&dataSem_);
        pthread_mutex_lock(&cmutex_);

        T temp = ringqueue_[cIndex_];
        cIndex_++;
        cIndex_ %= ringqueue_.size();

        pthread_mutex_unlock(&cmutex_);
        sem_post(&roomSem_);

        return temp;
    }

    ~RingQueue()
    {
        sem_destroy(&roomSem_);
        sem_destroy(&dataSem_);

        pthread_mutex_destroy(&pmutex_);
        pthread_mutex_destroy(&cmutex_);
    }
private:
    vector<T> ringqueue_;
    sem_t roomSem_;
    sem_t dataSem_;
    uint32_t pIndex_;
    uint32_t cIndex_;

    pthread_mutex_t pmutex_;
    pthread_mutex_t cmutex_;
};